#pragma  once

#include <string>
#include "json/json.h"
#include "property.h"

struct entity_t;




//////////////////////////////////////////////////////////////////////////
//		entity entityfactory_t
//////////////////////////////////////////////////////////////////////////
struct entityfactory_t
{
#ifdef LEO_DESIGNER
	int name_index;
	int get_name_index()
	{
		return ++name_index;
	}
#endif

	//	entityfactory_t(const char* i_typename, entityfactory_t* i_parent);
	entityfactory_t(stringhash i_typename, entityfactory_t* i_parent,const char* icon_name=NULL);
	virtual ~entityfactory_t()
	{
		for (unsigned int n=0; n<m_proparray.size(); ++n)
			delete m_proparray[n];
	}

	virtual entity_t* create() const=0;
	virtual entity_t* create(const entity_t& orig) const=0;

	const stringhash& get_typename() const
	{
		return type_name;
	}

	int get_typeid() const
	{
		return type_id;
	}

	bool isa(int i_typeid) const
	{
		const entityfactory_t* ptr=this;

		while (ptr)
		{
			if (ptr->type_id==i_typeid)
				return true;

			ptr=ptr->parent;
		}

		return false;
	}

	unsigned get_propertycount() const
	{
		return m_proparray.size();
	}

	property_t* get_property(unsigned i_index)
	{
		return m_proparray[i_index];
	}

	property_t* get_property(stringhash propname)
	{
		for (unsigned n=0; n<m_proparray.size(); ++n)
			if (propname.value==m_proparray[n]->name.value)
				return m_proparray[n];

		if (parent)
			return parent->get_property(propname);

		return NULL;
	}

	void add_property(property_t* p)
	{
		m_proparray.push_back(p);
	}

	void load_properties(void* i_object, json_object_t* prop_obj)
	{
		for (unsigned n=0; n<m_proparray.size(); ++n)
		{
#ifdef LEO_DESIGNER
			if (m_proparray[n]->m_editoronly)
				continue;
#endif

			m_proparray[n]->load_value(i_object,prop_obj);
		}

		if (parent)
			parent->load_properties(i_object,prop_obj);
	}

	string to_string(void* entity);
	static string to_string_propertydata(proptype i_type,void* i_data);


	entityfactory_t* get_parent()
	{
		return parent;
	}

	struct property_iterator
	{
		entityfactory_t* m_ptr;
		unsigned m_index;

		property_iterator():m_ptr(0),m_index(0){}
		property_iterator(entityfactory_t* i_ptr,unsigned i_index):m_ptr(i_ptr),m_index(i_index){}

		int operator==(const property_iterator& i_other) const{return (m_ptr==i_other.m_ptr && m_index==i_other.m_index);}
		int operator!=(const property_iterator& i_other) const{return (m_ptr!=i_other.m_ptr || m_index!=i_other.m_index);}
		void operator++(){++m_index; while (m_ptr && m_index>=m_ptr->get_propertycount()){m_index=0; m_ptr=m_ptr->get_parent();}}
		const property_t* operator->() const {return m_ptr->get_property(m_index);}
		const property_t& operator*() const {return *m_ptr->get_property(m_index);}
	};

	property_iterator begin()
	{
		property_iterator it;
		it.m_index=0;
		it.m_ptr=this;

		while (it.m_ptr && !it.m_ptr->get_propertycount())
			it.m_ptr=it.m_ptr->get_parent();

		return it;
	}

	property_iterator end()
	{
		return property_iterator(0,0);
	}

	entityfactory_t* prev;
	entityfactory_t* next;
	entityfactory_t* parent;
	entityfactory_t* child;
	entityfactory_t* next_sibling;

	//	const char* name;

#ifdef LEO_DESIGNER
	const char* icon_name;
#endif
	stringhash type_name;
	int type_id;
	vector<property_t*> m_proparray;
};






struct factorymanager_t
{
public:
	static void add_metaobject(entityfactory_t* i_creator)
	{
		i_creator->next=m_metaobjectlist;
		if (m_metaobjectlist)
			m_metaobjectlist->prev=i_creator;
		i_creator->prev=0;
		m_metaobjectlist=i_creator;
	}

	static int get_typeid()
	{
		return m_type_count++;
	}

	static entity_t* create_object(stringhash i_typename)
	{
		entityfactory_t* ptr=get_metaobject(i_typename);

		if (ptr)
			return ptr->create();

		return NULL;
	}

	static entityfactory_t* get_metaobject(int type_id)
	{
		entityfactory_t* ptr=m_metaobjectlist;

		while (ptr)
		{
			if (ptr->type_id==type_id)
			{
				return ptr;
			}

			ptr=ptr->next;
		}

		return NULL;
	}

	static entityfactory_t* get_metaobject(const stringhash& i_typename)
	{
		entityfactory_t* ptr;
		for (ptr=m_metaobjectlist; ptr && ptr->type_name.value!=i_typename.value; ptr=ptr->next){}
		return ptr;
	}

	static void init();

	static entityfactory_t* m_metaobjectlist;
	static int m_type_count;
};



template<typename ENTITY_TYPE,typename PROPERTY_TYPE> struct prop_binder
{
	prop_binder(stringhash i_name, unsigned i_offset,bool editoronly)
	{
		property_t* p=new PROPERTY_TYPE(i_name,i_offset);
		ENTITY_TYPE::get_class_metaobject()->add_property(p);
		p->data=0;
#ifdef LEO_DESIGNER
		p->m_editoronly=editoronly;
#endif
	}
	prop_binder(stringhash i_name, unsigned i_offset,const void* i_data,bool editoronly)
	{
		property_t* p=new PROPERTY_TYPE(i_name,i_offset);
		ENTITY_TYPE::get_class_metaobject()->add_property(p);
		p->data=(void*)i_data;
#ifdef LEO_DESIGNER
		p->m_editoronly=editoronly;
#endif
	}
#ifdef LEO_DESIGNER
	prop_binder(stringhash i_name, unsigned i_offset,const char* i_description,bool editoronly)
	{
		property_t* p=new PROPERTY_TYPE(i_name,i_offset);
		ENTITY_TYPE::get_class_metaobject()->add_property(p);
		p->m_description=i_description;
		p->data=0;
		p->m_editoronly=editoronly;
	}
#endif
};
#define BIND_PROPERTY(_object_,_variable_,_type_) prop_binder<_object_,_type_##_property_t> (#_variable_,(unsigned)offsetof(_object_,_variable_),false)
#define BIND_PROPERTY_DATA(_object_,_variable_,_type_,_data_) prop_binder<_object_,_type_##_property_t> (#_variable_,(unsigned)offsetof(_object_,_variable_),_data_,false)
#define BIND_PROPERTY_ARRAY(_object_,_variable_,_type_) \
{property_t* p=new array_property_t(#_variable_,(unsigned)offsetof(_object_,_variable_),new _type_##_property_t("DUMMY",0)); \
	add_property(p);}


#ifdef LEO_DESIGNER
#define BIND_PROPERTY_DESC(_object_,_variable_,_type_,_description_) prop_binder<_object_,_type_##_property_t> (#_variable_,(unsigned)offsetof(_object_,_variable_),_description_,false)
#define BIND_PROPERTY_EDITORONLY(_object_,_variable_,_type_) prop_binder<_object_,_type_##_property_t> (#_variable_,(unsigned)offsetof(_object_,_variable_),true)
#define BIND_PROPERTY_DATA_EDITORONLY(_object_,_variable_,_type_,_data_) prop_binder<_object_,_type_##_property_t> (#_variable_,(unsigned)offsetof(_object_,_variable_),_data_,true)
#else
#define BIND_PROPERTY_DESC(_object_,_variable_,_type_,_description_) BIND_PROPERTY(_object_,_variable_,_type_)
#define BIND_PROPERTY_EDITORONLY(_object_,_variable_,_type_) 
#define BIND_PROPERTY_DATA_EDITORONLY(_object_,_variable_,_type_,_data_)
#endif





#define DECLARE_ENTITY \
	virtual entityfactory_t* get_metaobject() const;\
	static entityfactory_t* get_class_metaobject();\
	static int get_class_typeid(){return get_class_metaobject()->get_typeid();}

#define  DEFINE_ROOT_ENTITY(_object_)\
struct _object_##_factory_t : entityfactory_t\
{\
	entity_t* create() const{return new _object_;}\
	entity_t* create(const entity_t& orig) const {return new _object_((const _object_&)orig);}\
	_object_##_factory_t():entityfactory_t(#_object_,NULL){bind_properties();}\
	void bind_properties();\
};\
	\
	_object_##_factory_t g_##_object_##_factory;\
	\
	entityfactory_t* _object_::get_metaobject() const\
{\
	return &g_##_object_##_factory;\
}\
	\
	entityfactory_t* _object_::get_class_metaobject()\
{\
	return &g_##_object_##_factory;\
}




#define  DEFINE_ENTITY(_object_,_parent_)\
struct _object_##_factory_t : entityfactory_t\
{\
	entity_t* create() const{return new _object_;}\
	entity_t* create(const entity_t& orig) const {return new _object_((const _object_&)orig);}\
	_object_##_factory_t():entityfactory_t(#_object_,_parent_::get_class_metaobject()){bind_properties();}\
	void bind_properties();\
};\
	\
	_object_##_factory_t g_##_object_##_factory;\
	\
	entityfactory_t* _object_::get_metaobject() const\
{\
	return &g_##_object_##_factory;\
}\
	\
	entityfactory_t* _object_::get_class_metaobject()\
{\
	return &g_##_object_##_factory;\
}

#define  DEFINE_ENTITY_ICON(_object_,_parent_,_icon_name_)\
struct _object_##_factory_t : entityfactory_t\
{\
	entity_t* create() const{return new _object_;}\
	entity_t* create(const entity_t& orig) const {return new _object_((const _object_&)orig);}\
	_object_##_factory_t():entityfactory_t(#_object_,_parent_::get_class_metaobject(),_icon_name_){bind_properties();}\
	void bind_properties();\
};\
	\
	_object_##_factory_t g_##_object_##_factory;\
	\
	entityfactory_t* _object_::get_metaobject() const\
{\
	return &g_##_object_##_factory;\
}\
	\
	entityfactory_t* _object_::get_class_metaobject()\
{\
	return &g_##_object_##_factory;\
}

#define PROPERTY_BINDING_BEGIN(_object_) void _object_##_factory_t :: bind_properties(){
#define PROPERTY_BINDING_END }

const char* const yes_no_list[]={"no","yes",0};


struct string_data_t
{
	const char* filter;
	const char* initdir;

	string_data_t(const char* f, const char* i):
	filter(f),
		initdir(i)
	{
	}
};

static const string_data_t texture_property_data("texture files(*.dds)|*.dds","textures");
static const string_data_t material_property_data("material files(*.mtl)|*.mtl","render/materials");
static const string_data_t scene_property_data("scene files(*.json)|*.json","Scenes");
static const string_data_t mesh_property_data("3d files(*.dae)|*.dae","");
static const string_data_t sound_property_data("sound files(*.wav)|*.wav","Sounds");

struct int_data_t
{
	int minimum;
	int maximum;

	int_data_t(int min,int max):minimum(min),maximum(max)
	{
	}
};

struct float_data_t
{
	float minimum;
	float maximum;
	int granularity;

	float_data_t(float min,float max, int g):minimum(min),maximum(max),granularity(g)
	{
	}
};
